//
// Ephi - simulation of magnetic fields and particles
// Copyright (C) 2007 Indrek Mandre <indrek(at)mare.ee>
// For more information please see http://www.mare.ee/indrek/
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//

#ifndef __threading_hpp__
#define __threading_hpp__

#ifdef THREADS_CPUCOUNT
  #define PTHREADS
#else
  #define THREADS_CPUCOUNT 1
#endif

#ifdef PTHREADS
  #include <pthread.h>
  #define HAVE_THREADS
#endif

#include <vector>

class Task
{
protected:
  virtual void execute () = 0;
  virtual ~Task () { }

  friend class TaskManager;

private:
  Task *next;
};

class Mutex
{
public:
  Mutex ();
  ~Mutex ();

  void lock ();
  void unlock ();
  bool trylock ();

private:
#ifdef PTHREADS
  pthread_mutex_t m;
#endif
  friend class Condition;
};

class AutoMutex
{
public:
  inline AutoMutex (Mutex& m) : m(m) { this->m.lock(); }
  inline AutoMutex (Mutex* m) : m(*m) { this->m.lock(); }
  inline ~AutoMutex () { m.unlock(); }

private:
  Mutex& m;

  // don't allow silly stuff
  AutoMutex (const AutoMutex& m);
  AutoMutex& operator= (const AutoMutex&);
};

class Condition
{
public:
  Condition (Mutex& m);
  ~Condition ();

  void wait ();

  void signal ();
  void broadcast ();

private:
  Mutex& m;
#ifdef PTHREADS
  pthread_cond_t cond;
#endif
};

class TaskManager
{
public:
  TaskManager (int threads = THREADS_CPUCOUNT);
  ~TaskManager ();

  void addTask (Task *task);

  Task *run();

private:
  Task *queue;
  Task *complete;
  Mutex lock;
  Condition work_cond;
  Condition ready_cond;
  volatile bool terminate;
  size_t working;

#ifdef PTHREADS
  typedef std::vector<pthread_t> threads_t;
  threads_t threads;
#endif

  static void *run_thread (void *ptr);
};

#endif // __threading_hpp__

